﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OBDUDSUtils.CNNMode;
using OBDUDSUtils.Common;

namespace OBDUDSUtils.EtasCAN
{
    public enum EtasCANStatus
    {
        FindDeviceError,
        NoFindDevice,
        OpenPortFail,
        OpenPortSuccess,
        ClosedPort
    }

    public class Es581Manage
    {
        public delegate void EsOBDMessage(List<CANBasic.VCI_CAN_OBJ> data);
        public  event EsOBDMessage EsOBDMessageEvent;
        public  event EsOBDMessage EsOBDMessageEvents
        {
            add
            {
                if (EsOBDMessageEvent == null)
                {
                    EsOBDMessageEvent += value;
                }
            }
            remove
            {
                EsOBDMessageEvent -= value;
            }
        }

        private  System.Timers.Timer revTimer;//定时器自动读取数据
        private  DiagData esData;//存储设备数据
        public  List<DiagMessageData> _OBDMessageDatas;//存储OBD报文

        private void EsSendOBDResult(List<CANBasic.VCI_CAN_OBJ> data)
        {
            EsOBDMessageEvent(data);
        }

        /// <summary>
        /// 初始化CAN
        /// </summary>
        /// <param name="device"></param>
        public void InitCANDevice(ref DiagData diagData)
        {
            esData = diagData;
            _OBDMessageDatas = new List<DiagMessageData>();

            revTimer = new System.Timers.Timer();
            revTimer.Interval = 10;
            revTimer.Enabled = false;
            revTimer.Elapsed += RevTimer_Elapsed;
        }

        private List<CANBasic.VCI_CAN_OBJ> EsReadMessage(DiagData esData)
        {
            icsSpyMessage[] stMessages = new icsSpyMessage[1024 * 1024];   //TempSpace for messages
            //long lResult;
            int lNumberOfMessages = 0;
            int lNumberOfErrors = 0;
            long lCount;
            icsSpyMessageJ1850 stJMsg = new icsSpyMessageJ1850();
            long iByteCount;
            int iLongMessageTotalByteCount;
            double dTime = 0;
            byte[] iDataBytes;
            List<CANBasic.VCI_CAN_OBJ> dataRes = new List<CANBasic.VCI_CAN_OBJ>();

            if (!esData.CANConnectStatus)
            {
                return dataRes;  // do not read messages if we haven't opened neoVI yet
            }
            // read the messages from the driver
            //icsSpyMessage[] icsSpyArray = new icsSpyMessage[1024 * 1024];
            var result = Etas581.icsneoGetMessages(esData.EtasDevice.m_hObject, ref stMessages, ref lNumberOfMessages, ref lNumberOfErrors);
            //var lResult = icsNeoDll.icsneoGetMessages(m_hObject, ref stMessages[0], ref lNumberOfMessages, ref lNumberOfErrors);

            // was the read successful?
            if (result == 1)
            {
                // clear the previous list of messages
                //lblReadCount.Text = "Number Read : " + Convert.ToString(lNumberOfMessages);
                //lblReadErrors.Text = "Number Errors : " + Convert.ToString(lNumberOfErrors);
                // for each message we read
                //dataRes.IsReadCompleted = true;
                for (lCount = 1; lCount <= lNumberOfMessages; lCount++)
                {
                    //Add the message to the list
                    if (stMessages[lCount - 1].ArbIDOrHeader != 0x7e8)
                    {
                        continue;
                    }
                    CANBasic.VCI_CAN_OBJ dataObj = new CANBasic.VCI_CAN_OBJ();
                    dataObj.Data = new byte[8];
                    // Calculate the messages timestamp
                    result = Etas581.icsneoGetTimeStampForMsg(esData.EtasDevice.m_hObject, ref stMessages[lCount - 1], ref dTime);
                    //sListString = "Time : " + Convert.ToString(dTime);  //Build String

                    // Was it a tx or rx message
                    if ((stMessages[lCount - 1].StatusBitField & Convert.ToInt32(eDATA_STATUS_BITFIELD_1.SPY_STATUS_TX_MSG)) > 0)
                    {
                        //sListString = sListString + " Tx ";
                    }
                    else
                    {
                        //sListString = sListString + " Rx ";
                    }

                    //Decode based on the protocol
                    switch (stMessages[lCount - 1].Protocol)
                    {
                        case (int)ePROTOCOL.SPY_PROTOCOL_CAN:
                            // list the arb id
                            //netWork = esNeoDevice.GetStringForNetworkID(Convert.ToInt16(stMessages[lCount - 1].NetworkID));
                            dataObj.ID = (uint)stMessages[lCount - 1].ArbIDOrHeader;
                            break;
                        case (int)ePROTOCOL.SPY_PROTOCOL_CANFD:
                        case (int)ePROTOCOL.SPY_PROTOCOL_ETHERNET:
                            if (stMessages[lCount - 1].ExtraDataPtrEnabled == 0)
                            {
                                //8 bytes of data
                                //dataRes.NetWork = icsNeoDll.GetStringForNetworkID(stMessages[lCount - 1].NetworkID);
                                dataObj.ID = (uint)stMessages[lCount - 1].ArbIDOrHeader;
                            }
                            else
                            {
                                if (stMessages[lCount - 1].Protocol == Convert.ToByte(ePROTOCOL.SPY_PROTOCOL_CANFD))
                                {
                                    //Count for CAN FD
                                    iLongMessageTotalByteCount = stMessages[lCount - 1].NumberBytesData;
                                }
                                else
                                {
                                    //Count for Ethernet
                                    iLongMessageTotalByteCount = ((stMessages[lCount - 1].NumberBytesHeader * 0x100) + stMessages[lCount - 1].NumberBytesData);
                                }

                                //More than 8 bytes of data
                                iDataBytes = new byte[iLongMessageTotalByteCount];

                                System.Runtime.InteropServices.Marshal.Copy(stMessages[lCount - 1].iExtraDataPtr, iDataBytes, 0, iLongMessageTotalByteCount);
                                System.Runtime.InteropServices.GCHandle gcHandle = System.Runtime.InteropServices.GCHandle.Alloc(iDataBytes, System.Runtime.InteropServices.GCHandleType.Pinned);

                                //dataRes.NetWork = icsNeoDll.GetStringForNetworkID(stMessages[lCount - 1].NetworkID);

                                if (stMessages[lCount - 1].Protocol == (int)ePROTOCOL.SPY_PROTOCOL_CANFD)
                                    // " FD ArbID : " + Convert.ToString(stMessages[lCount - 1].ArbIDOrHeader, 16);
                                for (iByteCount = 0; iByteCount < iLongMessageTotalByteCount; iByteCount++)
                                {
                                    //sListString = sListString + Convert.ToString(iDataBytes[iByteCount], 16) + " ";
                                }
                                gcHandle.Free();
                            }
                            break;
                        default:
                            // list the headers bytes
                            Etas581.ConvertCANtoJ1850Message(ref stMessages[lCount - 1], ref stJMsg);
                            //dataRes.NetWork = icsNeoDll.GetStringForNetworkID(stJMsg.NetworkID) + " Data : ";

                            //add the data bytes
                            //if (stJMsg.NumberBytesHeader >= 1)
                                // icsNeoDll.ConvertToHex(Convert.ToString(stJMsg.Header1)) + " ";
                            //if (stJMsg.NumberBytesHeader >= 2)
                                // icsNeoDll.ConvertToHex(Convert.ToString(stJMsg.Header2)) + " ";
                            //if (stJMsg.NumberBytesHeader >= 3)
                                // icsNeoDll.ConvertToHex(Convert.ToString(stJMsg.Header3)) + " ";
                            break;
                    }

                    if (stMessages[lCount - 1].NumberBytesData >= 1 && Convert.ToBoolean(stMessages[lCount - 1].ExtraDataPtrEnabled == 0))
                        dataObj.Data[0] = stMessages[lCount - 1].Data1;
                    if (stMessages[lCount - 1].NumberBytesData >= 2 && Convert.ToBoolean(stMessages[lCount - 1].ExtraDataPtrEnabled == 0))
                        dataObj.Data[1] = stMessages[lCount - 1].Data2;
                    if (stMessages[lCount - 1].NumberBytesData >= 3 && Convert.ToBoolean(stMessages[lCount - 1].ExtraDataPtrEnabled == 0))
                        dataObj.Data[2] = stMessages[lCount - 1].Data3;
                    if (stMessages[lCount - 1].NumberBytesData >= 4 && Convert.ToBoolean(stMessages[lCount - 1].ExtraDataPtrEnabled == 0))
                        dataObj.Data[3] = stMessages[lCount - 1].Data4;
                    if (stMessages[lCount - 1].NumberBytesData >= 5 && Convert.ToBoolean(stMessages[lCount - 1].ExtraDataPtrEnabled == 0))
                        dataObj.Data[4] = stMessages[lCount - 1].Data5;
                    if (stMessages[lCount - 1].NumberBytesData >= 6 && Convert.ToBoolean(stMessages[lCount - 1].ExtraDataPtrEnabled == 0))
                        dataObj.Data[5] = stMessages[lCount - 1].Data6;
                    if (stMessages[lCount - 1].NumberBytesData >= 7 && Convert.ToBoolean(stMessages[lCount - 1].ExtraDataPtrEnabled == 0))
                        dataObj.Data[6] = stMessages[lCount - 1].Data7;
                    if (stMessages[lCount - 1].NumberBytesData >= 8 && Convert.ToBoolean(stMessages[lCount - 1].ExtraDataPtrEnabled == 0))
                        dataObj.Data[7] = stMessages[lCount - 1].Data8;

                    dataObj.DataLen = 8;
                    dataRes.Add(dataObj);
                    
                    if (stMessages[lCount - 1].Protocol == (int)ePROTOCOL.SPY_PROTOCOL_LIN)
                    {
                        //"CS=" + stJMsg.ACK1.ToString("X2");
                    }
                    LogHelper.Log.Info($"接收：ID={dataObj.ID} data={BitConverter.ToString(dataObj.Data)}");

                    if (esData.IsSaveMessage)
                    {
                        DiagMessageData data = new DiagMessageData();
                        data.ID++;
                        data.Direction = "接收";
                        data.UpdateTime = System.DateTime.Now.ToString("yyyy.MM.dd HH:mm:ss");
                        data.FrameID = "0x" + Convert.ToString(dataObj.ID, 16).PadLeft(8, '0');
                        data.FrameFormat = "数据帧";
                        data.FrameType = "标准帧";
                        data.DataLength = "0x08";
                        data.FrameData = BitConverter.ToString(dataObj.Data).Replace("-", " ");
                        this._OBDMessageDatas.Add(data);
                    }
                }
            }
            else
            {
                //dataRes.IsReadCompleted = false;
            }
            return dataRes;
        }

        public bool EsSendMessage(DiagData esData)
        {
            icsSpyMessage stMessagesTx = new icsSpyMessage();
            long lNetworkID;

            // Has the uset open neoVI yet?;
            if (!esData.CANConnectStatus)
            {
                return false; // do not read messages if we haven't opened neoVI yet
            }
            // Read the Network we will transmit on (indicated by lstNetwork ListBox)
            lNetworkID = Etas581.GetNetworkIDfromString(esData.EtasDevice.EsNetWork);

            // Is this a CAN network or a J1850/ISO one?
            // load the message structure

            if (esData.EtasDevice.IsExtendedID)
            {
                //Make id Extended
                stMessagesTx.StatusBitField = Convert.ToInt16(eDATA_STATUS_BITFIELD_1.SPY_STATUS_XTD_FRAME);
            }
            else
            {
                //Use Normal ID
                stMessagesTx.StatusBitField = 0;
            }
            stMessagesTx.ArbIDOrHeader = (int)esData.EtasDevice.FrameID;// The ArbID
            stMessagesTx.NumberBytesData = (byte)esData.EtasDevice.EsNumberOfBytes;         // The number of Data Bytes
            if (stMessagesTx.NumberBytesData > 8) stMessagesTx.NumberBytesData = 8; // You can only have 8 databytes with CAN
                                                                                    // Load all of the data bytes in the structure

            stMessagesTx.Data1 = esData.EtasDevice.Data[0];
            stMessagesTx.Data2 = esData.EtasDevice.Data[1];
            stMessagesTx.Data3 = esData.EtasDevice.Data[2];
            stMessagesTx.Data4 = esData.EtasDevice.Data[3];
            stMessagesTx.Data5 = esData.EtasDevice.Data[4];
            stMessagesTx.Data6 = esData.EtasDevice.Data[5];
            stMessagesTx.Data7 = esData.EtasDevice.Data[6];
            stMessagesTx.Data8 = esData.EtasDevice.Data[7];

            // Transmit the assembled message
            var result = Etas581.icsneoTxMessages(esData.EtasDevice.m_hObject, ref stMessagesTx, Convert.ToInt32(lNetworkID), 1);
            //lResult = icsNeoDll.icsneoTxMessages(m_hObject, ref stMessagesTx, Convert.ToInt32(lNetworkID), 1);

            // Test the returned result
            if (result != 1)
            {
                LogHelper.Log.Info("Problem Transmitting Message");
                return false;
            }
            LogHelper.Log.Info($"Success Transmitting Message ID:{esData.EtasDevice.FrameID} data:{BitConverter.ToString(esData.EtasDevice.Data)}");
            
            if (esData.IsSaveMessage)
            {
                DiagMessageData message = new DiagMessageData();
                message.ID++;
                message.Direction = "发送";
                message.UpdateTime = System.DateTime.Now.ToString("yyyy.MM.dd HH:mm:ss");
                message.FrameID = "0x" + esData.EtasDevice.FrameID;
                message.FrameFormat = "数据帧";
                message.FrameType = "标准帧";
                message.DataLength = "0x08";
                message.FrameData = BitConverter.ToString(esData.EtasDevice.Data).Replace("-", " ");
                this._OBDMessageDatas.Add(message);
            }
            return true;
        }

        public void CloseEtas(ref DiagData esData)
        {
            int iNumberOfErrors = 0;
            var iResult = Etas581.icsneoClosePort(esData.EtasDevice.m_hObject, ref iNumberOfErrors);
            if (iResult == 1)
            {
                esData.CANConnectStatus = false;
                revTimer.Enabled = false;
            }
        }

        public void OpenEtas(ref DiagData _OBDData)
        {
            byte[] bNetwork = new byte[255]; //List of hardware IDs
            int iNumberOfDevices;   //Number of hardware devices to look for 
            int iCount;		 //counter

            //check if the port is already open
            if (_OBDData.CANConnectStatus)
            {
                return;
            }

            //File NetworkID array
            for (iCount = 0; iCount < 255; iCount++)
            {
                bNetwork[iCount] = Convert.ToByte(iCount);
            }

            //Set the number of devices to find, for this example look for 16.  This example will only work with the first.
            iNumberOfDevices = 15;

            //Search for connected hardware
            NeoDevice neoDev = new NeoDevice();

            var res = Etas581.icsneoFindNeoDevices((uint)eHardwareTypes.NEODEVICE_ALL, ref neoDev, ref iNumberOfDevices);
            //iResult = icsNeoDll.icsneoFindNeoDevices((uint)eHardwareTypes.NEODEVICE_ALL, ref neoDev, ref iNumberOfDevices);
            //iResult = icsNeoDll.icsneoFindAllUSBDevices(ref ndNeoToOpenex[0],ref iNumberOfDevices, 0, 0,ref neoDeviceOption, 0, 0);
            if (res == 0)
            {
                _OBDData.CANConnectStatus = false;
                return;
            }

            if (iNumberOfDevices < 1)
            {
                _OBDData.CANConnectStatus = false;
                return;
            }

            //ndNeoToOpen = ndNeoToOpenex[0].neoDevice;
            //Open the first found device
            res = Etas581.icsneoOpenNeoDevice(ref neoDev, ref _OBDData.EtasDevice.m_hObject, ref bNetwork, 1, 0);
            //iResult = icsNeoDll.icsneoOpenNeoDevice(ref neoDev, ref m_hObject, ref bNetwork[0], 1, 0);
            if (res == 1)
            {
                _OBDData.CANConnectStatus = true;

                //Set the device type for later use
                _OBDData.EtasDevice.OpenDeviceType = neoDev.DeviceType;
                revTimer.Enabled = true;
                return;
            }
            else
            {
                _OBDData.CANConnectStatus = false;
                esData = _OBDData;
                return;
            }
        }

        private void RevTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            var esResults = EsReadMessage(esData);
            if (esResults.Count <= 0)
                return;
            EsSendOBDResult(esResults);
        }

        public void SaveMessageData(string messageFilePath)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine(DiagMessageData.CONST_ID + DiagMessageData.CONST_SPACE + DiagMessageData.CONST_DIRECTION + DiagMessageData.CONST_SPACE + DiagMessageData.CONST_TIME + DiagMessageData.CONST_SPACE + DiagMessageData.CONST_FID + DiagMessageData.CONST_SPACE + DiagMessageData.CONST_FFORMAT + DiagMessageData.CONST_SPACE + DiagMessageData.CONST_FTYPE + DiagMessageData.CONST_SPACE + DiagMessageData.CONST_FLEN + DiagMessageData.CONST_SPACE + DiagMessageData.CONST_FDATA);

            foreach (var msg in this._OBDMessageDatas)
            {
                sb.AppendLine(msg.ID.ToString() + DiagMessageData.CONST_SPACE + msg.Direction + DiagMessageData.CONST_SPACE + msg.UpdateTime + DiagMessageData.CONST_SPACE + msg.FrameID + DiagMessageData.CONST_SPACE + msg.FrameFormat + DiagMessageData.CONST_SPACE + msg.FrameType + DiagMessageData.CONST_SPACE + msg.DataLength + DiagMessageData.CONST_SPACE + msg.FrameData);
            }
            DataFile.Data_SaveMessage(sb.ToString(), messageFilePath);
        }

        public void SaveTextData(string filePath, string data)
        {
            DataFile.Data_SaveMessage(data, filePath);
        }
    }
}
